package com.culturalCenter.dataCenter.globalconfig.exception;


import com.culturalCenter.dataCenter.Utils.ApiResult;
import com.culturalCenter.dataCenter.Utils.LogException;
import com.culturalCenter.dataCenter.Utils.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局统一异常处理类
 *
 * @author wulincheng
 * @Date 2020-7-23 09:49:53
 * 该类的处理逻辑:
 * （1）对请求参数异常和我们自定义的异常分别做单独处理
 * （2）对可能出现的未知异常进行单独处理
 */
@RestControllerAdvice
@Slf4j
public class GlobalException {

    /**
     * 可能出现的未知异常
     **/
    @ExceptionHandler(Exception.class)
    public ApiResult handle(HttpServletRequest req, Exception e) {
        LogException.LogError(log, req, "【系统异常】", e);
        log.error(e.getMessage(), e);
        return ApiResult.instance(ResultCode.FALL, e.toString());
    }

    /**
     * 参数异常 -- ConstraintViolationException()
     * 用于处理类似http://localhost:8080/user/getUser?age=30&name=yoyo请求中age和name的校验引发的异常
     **/
    @ExceptionHandler(ConstraintViolationException.class)
    public ApiResult urlParametersExceptionHandle(ConstraintViolationException e) {
        LogException.LogError(log, "【请求参数异常】", e);
        // 收集所有错误信息
        List<String> errorMsg = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.toList());
        return ApiResult.instance(ResultCode.REQUEST_PARAM_ERROR, e.toString());
    }

    /**
     * 参数异常 --- MethodArgumentNotValidException和BindException
     * MethodArgumentNotValidException --- 用于处理请求参数为实体类时校验引发的异常 --- Content-Type为application/json
     * BindException --- 用于处理请求参数为实体类时校验引发的异常  --- Content-Type为application/x-www-form-urlencoded
     **/
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public ApiResult bodyExceptionHandle(Exception e) {
        LogException.LogError(log, "【请求参数异常】", e);
        BindingResult bindingResult = null;
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
            bindingResult = ex.getBindingResult();
        } else {
            BindException ex = (BindException) e;
            ex.getBindingResult();
        }
        if (bindingResult != null) {
            // 收集所有错误信息
            List<String> errorMsg = bindingResult.getFieldErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList());
            return ApiResult.instance(ResultCode.REQUEST_PARAM_ERROR, e.toString());
        }

        return ApiResult.instance(ResultCode.REQUEST_PARAM_ERROR);
    }

    /**
     * 自定义异常 --- 自定义异常一般不要设置为ERROR级别,因为我们用自定义的异常主要是为了辅助我们处理业务逻辑,
     * --- 它们其实并不能被真正当作异常来看待
     **/
    @ExceptionHandler(ElegantException.class)
    public ApiResult elegantExceptionHandle(ElegantException e) {
        log.warn("【自定义异常】", e);
        return ApiResult.instance(e.getCode(), e.getMessage());
    }

}
